//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <ddk.h>
#include <ostimer.h>
#include <bulverde.h>
#include <intcbits.h>
#include <drvpub.h>
#include "wtdog.h"

WD_Tsk_Node g_WD_TskList[MAX_WD_TSK_NODE];
int g_tskcur = 0;
char *watchdog_string = "WATCHDOG MANAGER";
#define RCNR_SEC        __REG(0x40900000)

#define TIMER_M3_INT_CLR(pOSSR) (pOSSR = OSSR_M3)
#define TIMER_M3_INT_EN(pOIER)  (pOIER |= OIER_E3)
#define TIMER_M3_INT_DIS(pOIER) (pOIER &= ~OIER_E3)

/*
 * monitor the task node list
 */
static void watchdog_monitor(irq_t irq, void * pvDevice, InterruptContext * pContext)
{
    int i;
    TIMER_M3_INT_CLR(OSSR);
    TIMER_M3_INT_DIS(OIER);
    UInt32 rcnr_sec_val = RCNR_SEC;
    for (i = 0; i < g_tskcur; i++) {
        if (TSK_PING == g_WD_TskList[i].TskState) {
            if ( rcnr_sec_val <= (g_WD_TskList[i].PingStamp + WTD_FEED_TIME_SEC) ) {
                g_WD_TskList[i].TskState = TSK_FIRE;
                watchdog_xllp_fire();
                return;
            }
        }
    }
    WATCHDOG_MSG(0, kprintf("Watchdog: Would Refresh Timer.\n"));
    OSMR3 = OSCR + WTD_FEED_TIME_CLK; /* Feed the hardware watchdog */
    TIMER_M3_INT_EN(OIER);
}


int watchdog_init(void)
{
    /* Clear the OSSR watchdog bit*/
    int i;
    OSSR &= ~(OSSR_M3);

    if (OWER & OWER_WME) { /* If watchdog is already enable, disable it*/
        OWER &= ~(OWER_WME);
    }

    if (OIER & OIER_E3) { /* If OSMR3 interrupt has been enable, disable it*/
        OIER &= ~(OIER_E3);
    }

    for (i = 0; i < MAX_WD_TSK_NODE; i++) {
        g_WD_TskList[i].TskID = -1; /* This node hasn't been used */
        g_WD_TskList[i].PingID = -1;/* This task doesn't have watch point */
        g_WD_TskList[i].TskState = TSK_SLEEP;
        g_WD_TskList[i].TskString = NULL;
    }
    g_tskcur = 0;

    DzRegisterIsr(29/*OST_3*/, IPL6, (isr_t)watchdog_monitor, NULL);
    WATCHDOG_MSG(WDOG_MSG, kprintf("Watchdog: Initialize Succeeded!\n"));
    return 0;
}



int watchdog_exit(void)
{
    return 0;
}

int watchdog_reset(void)
{
    return 0;
}

int watchdog_sleep(int TskID)
{
    /* If the Task has't been monitored by watchdog
       This task id is in the queue
    */
    if (TskID < g_tskcur) {
        g_WD_TskList[TskID].TskState = TSK_SLEEP;
        return 0;
    }
    /* Task id is invaild */
    return -1;
}

/*
 *	If one moudle request a watchdog node, use watchdog_open
 *  "TaskString" is moudle descriptor
 *  return value is the alloced task id
 */
int watchdog_open(char *TaskString)
{
    if (g_tskcur < MAX_WD_TSK_NODE) {
        g_WD_TskList[g_tskcur].TskID = g_tskcur; /* Alloc the node's position as task id */
        g_WD_TskList[g_tskcur].TskState = TSK_WATCH; /* Watchdog begin to watch this task */
        g_WD_TskList[g_tskcur].TskString = TaskString;
        /* Watchdog has at least one task node, so enable OSMR3 interrupt */
        if (NULL != TaskString) {
            WATCHDOG_MSG(WDOG_MSG, kprintf("Watchdog: %s!\n", TaskString));
        }
        if (g_tskcur == 0) {
            OSMR3 = OSCR + WTD_FEED_TIME_CLK;
            OIER |= OIER_E3;
        }
        g_tskcur++;
        return g_WD_TskList[g_tskcur].TskID;
    }
    /* The task queue is full */
	return -1;
}

void watchdog_feed(int TskID, int PingID)
{
    g_WD_TskList[TskID].PingID = PingID;
    g_WD_TskList[TskID].TskState = TSK_FEED; /* Feed watchdog */
}

/*
 *	If the timer expires.. Used for myself test watchdog
 */
void watchdog_xllp_fire(void)
{
	WATCHDOG_MSG(WDOG_MSG, kprintf("Watchdog: Would Reboot.\n"));
    TIMER_M3_INT_CLR(OSSR);
    TIMER_M3_INT_DIS(OIER);
    OWER |= OWER_WME; /* Enable OSMR3 as watchdog timer */
    //OSMR3 = OSCR + 40000/*32500*//*10ms*/;
    OSMR3 = OSCR + 40000;/* Watchdog Reboot at once*/
}


/*
 *	Refresh the time stamp.
 */
void watchdog_ping(int TskID)
{
    g_WD_TskList[TskID].PingStamp = RCNR_SEC; /* Set the ping time stamp sec as uint*/
    g_WD_TskList[TskID].TskState = TSK_PING; /* Watchdog begin to watch this task */
}



